/*******************************************************************************
 * Copyright (c) 2008 The University of York.
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * Contributors:
 *     Dimitrios Kolovos - initial API and implementation
******************************************************************************/
package org.eclipse.epsilon.egl.servlet;

import java.io.File;
import java.io.IOException;
import java.util.Map.Entry;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.epsilon.common.parse.problem.ParseProblem;
import org.eclipse.epsilon.egl.EglTemplate;
import org.eclipse.epsilon.egl.EglTemplateFactory;
import org.eclipse.epsilon.eol.execute.context.FrameStack;
import org.eclipse.epsilon.eol.execute.context.Variable;

/**
 * TODO: Dispose models, Register Ecore and load by M2 uri
 * @author dkolovos
 *
 */
public class EglServlet extends HttpServlet {
	
	// Generated by Eclipse
	private static final long serialVersionUID = 4323517000140715630L;
	
	protected static ModelManager modelManager = new ModelManager();
	protected static CacheFacade caching = new CacheFacade();

	protected String templateVirtualPath;
	protected File templateRealLocation;
	
	@SuppressWarnings("unchecked")
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		
		modelManager.setServletContext(this.getServletContext());
		
		if (req.getContextPath().trim().isEmpty()) {
			templateVirtualPath = req.getRequestURI().substring(1);
		} else {
			templateVirtualPath = req.getRequestURI().substring(req.getRequestURI().indexOf('/',1)+1);
		}
		
		String identifier = templateVirtualPath;
		
		for (Entry<String, String[]> entry : (Set<Entry<String, String[]>>)req.getParameterMap().entrySet()) {
			identifier += entry.getKey();
			identifier += "=";
			identifier += entry.getValue()[0];
		}
				
		if (caching.pageCache.isCached(identifier)) {
			resp.setContentType("text/html");
			resp.getWriter().println(caching.pageCache.retrieve(identifier));
			
		} else {
			EglTemplateFactory factory = createTemplateFactory();
			EglTemplate template;
			
			try {
				try {
					templateRealLocation = new File(this.getServletContext().getRealPath(templateVirtualPath));
					
					if (templateRealLocation == null || !templateRealLocation.exists()) {
						resp.sendError(404, req.getRequestURI());
						return;
					}
					
					template = factory.load(templateRealLocation);
					
				} catch (Exception e) {
					req.setAttribute("javax.servlet.error.exception", e);
					resp.sendError(500);
					return;
				}
				
				if (template.getParseProblems().size() > 0) {
					String message = "Syntax error(s) in ";
					for (ParseProblem problem : template.getParseProblems()) {
						message += problem.toString() + "\n";
					}
					resp.sendError(500, message);
					return;
				}
				
				modelManager.setCurrentModelRepository(factory.getContext().getModelRepository());
				
				factory.getContext().getFrameStack().put(
					// Create built-in variables
					Variable.createReadOnlyVariable("modelManager", modelManager),
					Variable.createReadOnlyVariable("cache", caching),
					// Create JSP-like built-in variables
					Variable.createReadOnlyVariable("request", req),
					Variable.createReadOnlyVariable("response", resp),
					Variable.createReadOnlyVariable("config", getServletConfig()),
					Variable.createReadOnlyVariable("application", getServletContext()),
					Variable.createReadOnlyVariable("session", req.getSession())
				);

				String result = template.process();
				
				resp.setContentType("text/html");
				resp.getWriter().println(result);

				caching.pageCache.cache(identifier, result);
				
			} catch (Exception e) {
				req.setAttribute("javax.servlet.error.exception", e);
				resp.sendError(500);
			} finally {
				try {
					factory.getContext().dispose();
				}
				catch (Exception ex) {
					// We can only do so much...
				}
			}
		}
		
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		
		doGet(req, resp);
		
	}
	
	
	protected EglTemplateFactory createTemplateFactory() {
		EglTemplateFactory factory = null;
		
		final String clazz = getInitParameter("TemplateFactory");
		if (clazz != null) {
			factory = instantiateTemplateFactory(clazz);
		}
		
		if (factory == null) {
			factory = createDefaultTemplateFactory();
		}
		
		return factory;
	}

	protected EglTemplateFactory instantiateTemplateFactory(String clazz) {
		try {
			return (EglTemplateFactory) Class.forName(clazz).getConstructor().newInstance();
		
		} catch (Exception e) {
			System.out.println("Could not instantiate " + clazz);
			e.printStackTrace();
			
			return null;
		}
	}

	protected EglTemplateFactory createDefaultTemplateFactory() {
		return new EglTemplateFactory();
	}
}
